#!/usr/bin/env python3
"""
全面的过滤器测试套件 - 200+测试用例
测试所有功能组合：图表类型、过滤器、聚合、分段、排序等
"""

import json
import time
import requests
from datetime import datetime
import random
from typing import Dict, List, Any
import itertools


class Comprehensive200Tests:
    def __init__(self):
        self.base_url = "http://localhost:5000"
        self.test_cases = []
        self.results = []
        
        # 定义所有配置选项
        self.chart_types = ["bar", "line", "pie", "doughnut", "polarArea", "radar"]
        
        self.all_fields = [
            "index", "trade_id", "symbol", "direction", "side", "trade_type", 
            "session", "nth_trade", "open_price", "close_price", "open_qty", 
            "close_qty", "open_datetime", "close_datetime", "holding_time", 
            "open_order_type", "close_order_type", "net_profit", "gross_profit", 
            "commission", "point_profit_per_lot", "total_points", 
            "max_floating_profit_points", "max_floating_profit_dollars", 
            "max_floating_loss_points", "max_floating_loss_dollars", 
            "entry_reason", "exit_reason", "stop_loss_price", "stop_loss_reason", 
            "take_profit_price", "take_profit_reason", "rating", "evaluation", 
            "open_rrr", "close_rrr", "fwr", "mood", "notes"
        ]
        
        self.numeric_fields = [
            "index", "nth_trade", "open_price", "close_price", "open_qty", 
            "close_qty", "point_profit_per_lot", "total_points", "gross_profit", 
            "net_profit", "commission", "max_floating_profit_points", 
            "max_floating_profit_dollars", "max_floating_loss_points", 
            "max_floating_loss_dollars", "stop_loss_price", "take_profit_price", 
            "rating", "open_rrr", "close_rrr", "fwr"
        ]
        
        self.string_fields = [
            "trade_id", "symbol", "direction", "side", "trade_type", "session", 
            "open_order_type", "close_order_type", "entry_reason", "exit_reason", 
            "stop_loss_reason", "take_profit_reason", "evaluation", "mood", "notes"
        ]
        
        self.datetime_fields = ["open_datetime", "close_datetime"]
        
        self.operators = [
            "equals", "not_equals", "contains", "not_contains", "greater_than", 
            "less_than", "greater_than_or_equal", "less_than_or_equal", 
            "is_null", "is_not_null", "is_empty", "is_not_empty"
        ]
        
        self.aggregations = ["sum", "avg", "max", "min", "count", "median", "stddev"]
        
        self.y_fields = [
            "count", "win_rate", "profit_factor", "average_win", "average_loss",
            "net_profit", "gross_profit", "commission", "point_profit_per_lot",
            "total_points", "max_floating_profit_points", "max_floating_profit_dollars",
            "max_floating_loss_points", "max_floating_loss_dollars", "rating",
            "open_rrr", "close_rrr", "fwr"
        ]
        
        self.sort_options = ["label_asc", "label_desc", "value_asc", "value_desc", "count_desc"]
        
        self.binning_methods = ["fixed_size", "fixed_count", "percentile", "auto"]
        
        self.binning_formats = ["range", "center", "lower", "upper"]
        
        # 测试值集合
        self.test_values = {
            "symbol": ["MESM25", "MESU25", "MNQM25", "AAPL", "TSLA", "MSFT"],
            "direction": ["Long", "Short"],
            "side": ["Left", "Right", "Mid"],
            "trade_type": ["Scalp", "Swing", "Position", "Intraday"],
            "session": ["RTH", "ETH"],
            "entry_reason": ["赌突破", "赌反转", "breakout", "DBDT"],
            "exit_reason": ["达到目标位", "达到止损位", "符合预期-盈利离场"],
            "evaluation": ["优秀", "良好", "一般"],
            "mood": ["开心", "沮丧", "平静"]
        }
        
        self.numeric_test_values = [0, 1, 10, 100, 1000, -100, -1000]
        
    def generate_all_test_cases(self):
        """生成所有测试用例"""
        print("开始生成全面的测试用例...")
        
        # 1. 图表类型 x 字段组合测试 (6 x 10 = 60个)
        self.generate_chart_field_combinations()
        
        # 2. 单字段过滤器测试 (30个字段 x 不同操作符 = 80个)
        self.generate_single_field_filter_tests()
        
        # 3. 多字段过滤器组合测试 (30个)
        self.generate_multi_field_filter_tests()
        
        # 4. 聚合方式测试 (7种聚合 x 5个字段 = 35个)
        self.generate_aggregation_tests()
        
        # 5. 分段配置测试 (4种方法 x 4种格式 = 16个)
        self.generate_binning_tests()
        
        # 6. 排序配置测试 (5种排序 x 5个字段 = 25个)
        self.generate_sorting_tests()
        
        # 7. 边界和特殊情况测试 (20个)
        self.generate_edge_case_tests()
        
        print(f"总共生成了 {len(self.test_cases)} 个测试用例")
    
    def generate_chart_field_combinations(self):
        """生成图表类型和字段的组合测试"""
        print("生成图表类型和字段组合测试...")
        
        # 选择常用的字段组合
        x_field_samples = ["symbol", "direction", "session", "trade_type", "weekday", 
                          "month", "open_date", "rating", "evaluation", "side"]
        y_field_samples = ["count", "net_profit", "gross_profit", "win_rate", "profit_factor"]
        
        for chart_type in self.chart_types:
            for x_field in x_field_samples:
                # 为每个x字段选择2个y字段
                for y_field in random.sample(y_field_samples, 2):
                    self.test_cases.append({
                        "name": f"图表组合_{chart_type}_{x_field}_{y_field}",
                        "chart_type": chart_type,
                        "x_field": x_field,
                        "y_field": y_field,
                        "x_aggregation": "count",
                        "y_aggregation": "sum" if y_field not in ["count", "win_rate", "profit_factor"] else "count",
                        "filters": [],
                        "expected_result": {
                            "type": "chart_combination_test",
                            "should_have_data": True
                        }
                    })
    
    def generate_single_field_filter_tests(self):
        """生成单字段过滤器测试"""
        print("生成单字段过滤器测试...")
        
        # 为每个字段类型测试兼容的操作符
        for field in self.string_fields:
            # 字符串字段测试
            compatible_ops = ["equals", "not_equals", "contains", "not_contains", "is_null", "is_not_null"]
            for operator in compatible_ops:
                value = self.get_test_value(field, operator)
                self.test_cases.append({
                    "name": f"字符串过滤_{field}_{operator}",
                    "chart_type": "bar",
                    "x_field": "symbol",
                    "y_field": "count",
                    "filters": [{"field": field, "operator": operator, "value": value, "logic": "and"}],
                    "expected_result": {
                        "type": "string_filter_test",
                        "should_have_data": operator not in ["is_null", "is_empty"]
                    }
                })
        
        # 数值字段测试
        for field in self.numeric_fields[:10]:  # 只取前10个数值字段避免太多
            compatible_ops = ["equals", "not_equals", "greater_than", "less_than", 
                            "greater_than_or_equal", "less_than_or_equal", "is_null", "is_not_null"]
            for operator in compatible_ops:
                value = self.get_test_value(field, operator)
                self.test_cases.append({
                    "name": f"数值过滤_{field}_{operator}",
                    "chart_type": "bar",
                    "x_field": "symbol",
                    "y_field": "count",
                    "filters": [{"field": field, "operator": operator, "value": value, "logic": "and"}],
                    "expected_result": {
                        "type": "numeric_filter_test",
                        "should_have_data": True
                    }
                })
    
    def generate_multi_field_filter_tests(self):
        """生成多字段过滤器组合测试"""
        print("生成多字段过滤器组合测试...")
        
        # AND组合测试
        and_combinations = [
            [("symbol", "equals", "MESM25"), ("direction", "equals", "Long")],
            [("session", "equals", "RTH"), ("net_profit", "greater_than", "0")],
            [("trade_type", "equals", "Scalp"), ("rating", "greater_than", "0")],
            [("side", "equals", "Left"), ("gross_profit", "less_than", "1000")],
            [("direction", "equals", "Short"), ("session", "equals", "ETH"), ("net_profit", "less_than", "0")],
        ]
        
        for i, combination in enumerate(and_combinations):
            filters = []
            for j, (field, operator, value) in enumerate(combination):
                logic = "and" if j < len(combination) - 1 else "and"
                filters.append({"field": field, "operator": operator, "value": value, "logic": logic})
            
            self.test_cases.append({
                "name": f"AND组合_{i+1}_{len(combination)}个条件",
                "chart_type": "bar",
                "x_field": "symbol",
                "y_field": "count",
                "filters": filters,
                "expected_result": {
                    "type": "and_combination_test",
                    "should_have_data": True
                }
            })
        
        # OR组合测试
        or_combinations = [
            [("symbol", "equals", "MESM25"), ("symbol", "equals", "MESU25")],
            [("direction", "equals", "Long"), ("direction", "equals", "Short")],
            [("session", "equals", "RTH"), ("session", "equals", "ETH")],
            [("side", "equals", "Left"), ("side", "equals", "Right")],
            [("trade_type", "equals", "Scalp"), ("trade_type", "equals", "Swing"), ("trade_type", "equals", "Position")],
        ]
        
        for i, combination in enumerate(or_combinations):
            filters = []
            for j, (field, operator, value) in enumerate(combination):
                logic = "or" if j < len(combination) - 1 else "and"
                filters.append({"field": field, "operator": operator, "value": value, "logic": logic})
            
            self.test_cases.append({
                "name": f"OR组合_{i+1}_{len(combination)}个条件",
                "chart_type": "bar",
                "x_field": "symbol",
                "y_field": "count",
                "filters": filters,
                "expected_result": {
                    "type": "or_combination_test",
                    "should_have_data": True
                }
            })
    
    def generate_aggregation_tests(self):
        """生成聚合方式测试"""
        print("生成聚合方式测试...")
        
        test_fields = ["net_profit", "gross_profit", "commission", "total_points", "rating"]
        
        for aggregation in self.aggregations:
            for field in test_fields:
                for chart_type in ["bar", "line", "pie"]:
                    self.test_cases.append({
                        "name": f"聚合测试_{aggregation}_{field}_{chart_type}",
                        "chart_type": chart_type,
                        "x_field": "symbol",
                        "y_field": field,
                        "x_aggregation": "count",
                        "y_aggregation": aggregation,
                        "filters": [],
                        "expected_result": {
                            "type": "aggregation_test",
                            "aggregation": aggregation,
                            "should_have_data": True
                        }
                    })
    
    def generate_binning_tests(self):
        """生成分段配置测试"""
        print("生成分段配置测试...")
        
        binning_fields = ["net_profit", "gross_profit", "total_points", "rating"]
        
        for method in self.binning_methods:
            for format_type in self.binning_formats:
                for field in binning_fields:
                    # 设置分段参数
                    binning_values = {
                        "fixed_size": 100,
                        "fixed_count": 10,
                        "percentile": 20,
                        "auto": 15
                    }
                    
                    self.test_cases.append({
                        "name": f"分段测试_{method}_{format_type}_{field}",
                        "chart_type": "bar",
                        "x_field": field,
                        "y_field": "count",
                        "x_aggregation": "count",
                        "y_aggregation": "count",
                        "filters": [],
                        "x_enable_binning": True,
                        "x_binning_method": method,
                        "x_binning_value": binning_values[method],
                        "x_binning_format": format_type,
                        "x_binning_include_empty": random.choice([True, False]),
                        "x_binning_round_values": True,
                        "expected_result": {
                            "type": "binning_test",
                            "method": method,
                            "format": format_type,
                            "should_have_data": True
                        }
                    })
    
    def generate_sorting_tests(self):
        """生成排序配置测试"""
        print("生成排序配置测试...")
        
        test_fields = ["symbol", "direction", "session", "trade_type", "rating"]
        
        for sort_option in self.sort_options:
            for field in test_fields:
                for max_items in [10, 20, -1]:  # 测试不同的最大显示数量
                    self.test_cases.append({
                        "name": f"排序测试_{sort_option}_{field}_{max_items}",
                        "chart_type": "bar",
                        "x_field": field,
                        "y_field": "count",
                        "x_aggregation": "count",
                        "y_aggregation": "count",
                        "filters": [],
                        "x_sort": sort_option,
                        "x_max_items": max_items,
                        "x_include_empty": random.choice([True, False]),
                        "expected_result": {
                            "type": "sorting_test",
                            "sort_option": sort_option,
                            "should_have_data": True
                        }
                    })
    
    def generate_edge_case_tests(self):
        """生成边界和特殊情况测试"""
        print("生成边界和特殊情况测试...")
        
        # 空值测试
        null_test_fields = ["rating", "evaluation", "mood", "notes", "stop_loss_price"]
        for field in null_test_fields:
            self.test_cases.append({
                "name": f"空值测试_{field}",
                "chart_type": "bar",
                "x_field": "symbol",
                "y_field": "count",
                "filters": [{"field": field, "operator": "is_null", "value": "", "logic": "and"}],
                "expected_result": {
                    "type": "null_test",
                    "should_have_data": True
                }
            })
        
        # 非空值测试
        for field in null_test_fields:
            self.test_cases.append({
                "name": f"非空值测试_{field}",
                "chart_type": "bar",
                "x_field": "symbol",
                "y_field": "count",
                "filters": [{"field": field, "operator": "is_not_null", "value": "", "logic": "and"}],
                "expected_result": {
                    "type": "not_null_test",
                    "should_have_data": True
                }
            })
        
        # 极值测试
        extreme_tests = [
            ("net_profit", "greater_than", "10000"),  # 极大值
            ("net_profit", "less_than", "-10000"),   # 极小值
            ("rating", "equals", "10"),              # 可能不存在的评分
            ("total_points", "greater_than", "1000") # 大点数
        ]
        
        for field, operator, value in extreme_tests:
            self.test_cases.append({
                "name": f"极值测试_{field}_{operator}_{value}",
                "chart_type": "bar",
                "x_field": "symbol",
                "y_field": "count",
                "filters": [{"field": field, "operator": operator, "value": value, "logic": "and"}],
                "expected_result": {
                    "type": "extreme_test",
                    "should_have_data": False  # 极值测试可能没有数据
                }
            })
        
        # 文本搜索测试
        text_tests = [
            ("symbol", "contains", "MES"),
            ("entry_reason", "contains", "突破"),
            ("exit_reason", "contains", "止损"),
            ("trade_id", "not_contains", "XXX")
        ]
        
        for field, operator, value in text_tests:
            self.test_cases.append({
                "name": f"文本搜索_{field}_{operator}_{value}",
                "chart_type": "bar",
                "x_field": "symbol",
                "y_field": "count",
                "filters": [{"field": field, "operator": operator, "value": value, "logic": "and"}],
                "expected_result": {
                    "type": "text_search_test",
                    "should_have_data": True
                }
            })
    
    def get_test_value(self, field: str, operator: str):
        """获取测试值"""
        if operator in ["is_null", "is_not_null", "is_empty", "is_not_empty"]:
            return ""
        
        if field in self.test_values:
            return random.choice(self.test_values[field])
        
        if field in self.numeric_fields:
            if operator in ["greater_than", "less_than", "greater_than_or_equal", "less_than_or_equal"]:
                return str(random.choice([0, 100, 1000, -100]))
            else:
                return str(random.choice([1, 5, 10]))
        
        return "test_value"
    
    def run_test_case(self, test_case: Dict) -> Dict:
        """运行单个测试用例"""
        try:
            # 构建请求数据
            request_data = {
                "config": json.dumps(test_case)
            }
            
            # 发送GET请求，配置作为URL参数
            response = requests.get(
                f"{self.base_url}/statistics/chart_data",
                params=request_data,
                timeout=30
            )
            
            # 检查响应
            if response.status_code == 200:
                result_data = response.json()
                
                # 验证结果
                validation_result = self.validate_result(test_case, result_data)
                
                return {
                    "test_case": test_case["name"],
                    "status": "PASS" if validation_result["valid"] else "FAIL",
                    "response_time": response.elapsed.total_seconds(),
                    "data_count": len(result_data.get("trades", [])),
                    "total_trades": result_data.get("total", 0),
                    "validation": validation_result,
                    "details": {
                        "chart_type": test_case.get("chart_type", "N/A"),
                        "x_field": test_case.get("x_field", "N/A"),
                        "y_field": test_case.get("y_field", "N/A"),
                        "filter_count": len(test_case.get("filters", [])),
                        "response_size": len(json.dumps(result_data))
                    }
                }
            else:
                return {
                    "test_case": test_case["name"],
                    "status": "ERROR",
                    "error": f"HTTP {response.status_code}",
                    "response_time": response.elapsed.total_seconds(),
                    "data_count": 0,
                    "total_trades": 0,
                    "validation": {"valid": False, "message": "HTTP error"},
                    "details": test_case
                }
        
        except Exception as e:
            return {
                "test_case": test_case["name"],
                "status": "ERROR",
                "error": str(e)[:100],
                "response_time": 0,
                "data_count": 0,
                "total_trades": 0,
                "validation": {"valid": False, "message": f"Exception: {str(e)[:50]}"},
                "details": test_case
            }
    
    def validate_result(self, test_case: Dict, result_data: Dict) -> Dict:
        """验证测试结果"""
        expected = test_case["expected_result"]
        
        # 基础验证
        if "trades" not in result_data:
            return {"valid": False, "message": "结果中没有trades字段"}
        
        # 检查数据是否为空
        has_data = len(result_data["trades"]) > 0
        
        if expected["should_have_data"] and not has_data:
            return {"valid": False, "message": "期望有数据但实际为空"}
        
        if not expected["should_have_data"] and has_data:
            # 对于期望没有数据的情况，如果有数据也不算错误，只是记录
            pass
        
        return {"valid": True, "message": "验证通过"}
    
    def run_all_tests(self):
        """运行所有测试"""
        print(f"开始运行 {len(self.test_cases)} 个测试用例...")
        
        start_time = time.time()
        batch_size = 50  # 每批次运行50个测试
        
        for batch_start in range(0, len(self.test_cases), batch_size):
            batch_end = min(batch_start + batch_size, len(self.test_cases))
            batch_cases = self.test_cases[batch_start:batch_end]
            
            print(f"\n运行第 {batch_start//batch_size + 1} 批次: 测试 {batch_start+1}-{batch_end}")
            
            batch_results = []
            for i, test_case in enumerate(batch_cases):
                test_num = batch_start + i + 1
                print(f"  运行测试 {test_num}/{len(self.test_cases)}: {test_case['name'][:50]}...")
                
                result = self.run_test_case(test_case)
                batch_results.append(result)
                self.results.append(result)
                
                # 显示简要结果
                if result["status"] == "PASS":
                    print(f"    ✓ {result['total_trades']}条交易 {result['response_time']:.2f}s")
                else:
                    print(f"    ✗ {result['status']}")
                
                # 短暂延迟
                time.sleep(0.05)
            
            # 显示批次统计
            batch_passed = sum(1 for r in batch_results if r["status"] == "PASS")
            print(f"  批次结果: {batch_passed}/{len(batch_results)} 通过")
        
        end_time = time.time()
        
        # 生成报告
        self.generate_report(end_time - start_time)
    
    def generate_report(self, total_time: float):
        """生成测试报告"""
        print(f"\n{'='*80}")
        print("全面测试报告 - 200+测试用例")
        print(f"{'='*80}")
        
        total_tests = len(self.results)
        passed_tests = sum(1 for r in self.results if r["status"] == "PASS")
        failed_tests = sum(1 for r in self.results if r["status"] == "FAIL")
        error_tests = sum(1 for r in self.results if r["status"] == "ERROR")
        
        print(f"总测试数: {total_tests}")
        print(f"通过: {passed_tests} ({passed_tests/total_tests*100:.1f}%)")
        print(f"失败: {failed_tests} ({failed_tests/total_tests*100:.1f}%)")
        print(f"错误: {error_tests} ({error_tests/total_tests*100:.1f}%)")
        print(f"总耗时: {total_time:.2f}秒")
        print(f"平均每个测试: {total_time/total_tests:.2f}秒")
        
        # 按测试类型统计
        test_types = {}
        for result in self.results:
            test_name = result["test_case"]
            test_type = test_name.split("_")[0] if "_" in test_name else "其他"
            if test_type not in test_types:
                test_types[test_type] = {"total": 0, "passed": 0, "failed": 0, "error": 0}
            
            test_types[test_type]["total"] += 1
            if result["status"] == "PASS":
                test_types[test_type]["passed"] += 1
            elif result["status"] == "FAIL":
                test_types[test_type]["failed"] += 1
            else:
                test_types[test_type]["error"] += 1
        
        print(f"\n按测试类型统计:")
        for test_type, stats in sorted(test_types.items()):
            success_rate = stats['passed']/stats['total']*100
            print(f"  {test_type}: {stats['passed']}/{stats['total']} 通过 ({success_rate:.1f}%)")
        
        # 性能统计
        response_times = [r["response_time"] for r in self.results if r["response_time"] > 0]
        if response_times:
            print(f"\n性能统计:")
            print(f"  最快响应: {min(response_times):.3f}秒")
            print(f"  最慢响应: {max(response_times):.3f}秒")
            print(f"  平均响应: {sum(response_times)/len(response_times):.3f}秒")
            print(f"  中位数响应: {sorted(response_times)[len(response_times)//2]:.3f}秒")
        
        # 数据量统计
        total_trades_counts = [r["total_trades"] for r in self.results if r["total_trades"] > 0]
        if total_trades_counts:
            print(f"\n数据量统计:")
            print(f"  最少记录: {min(total_trades_counts)}")
            print(f"  最多记录: {max(total_trades_counts)}")
            print(f"  平均记录: {sum(total_trades_counts)/len(total_trades_counts):.1f}")
        
        # 显示前10个失败的测试
        failed_results = [r for r in self.results if r["status"] == "FAIL"]
        if failed_results:
            print(f"\n失败的测试 (前10个):")
            for result in failed_results[:10]:
                print(f"  - {result['test_case']}: {result['validation']['message']}")
        
        # 显示前5个错误的测试
        error_results = [r for r in self.results if r["status"] == "ERROR"]
        if error_results:
            print(f"\n错误的测试 (前5个):")
            for result in error_results[:5]:
                print(f"  - {result['test_case']}: {result['error']}")
        
        # 保存详细报告
        self.save_detailed_report()
        
        # 总结
        print(f"\n{'='*80}")
        if passed_tests >= total_tests * 0.95:  # 95%通过率
            print("🎉 优秀！测试通过率超过95%")
        elif passed_tests >= total_tests * 0.90:  # 90%通过率
            print("✅ 良好！测试通过率超过90%")
        elif passed_tests >= total_tests * 0.80:  # 80%通过率
            print("⚠️  一般，测试通过率超过80%，需要改进")
        else:
            print("❌ 测试通过率较低，需要重点修复")
        
        print(f"全面测试完成！共验证了 {total_tests} 个功能点")
    
    def save_detailed_report(self):
        """保存详细测试报告"""
        report_data = {
            "timestamp": datetime.now().isoformat(),
            "summary": {
                "total_tests": len(self.results),
                "passed": sum(1 for r in self.results if r["status"] == "PASS"),
                "failed": sum(1 for r in self.results if r["status"] == "FAIL"),
                "errors": sum(1 for r in self.results if r["status"] == "ERROR"),
                "success_rate": sum(1 for r in self.results if r["status"] == "PASS") / len(self.results) * 100
            },
            "test_cases": self.test_cases,
            "results": self.results
        }
        
        with open("comprehensive_200_test_report.json", "w", encoding="utf-8") as f:
            json.dump(report_data, f, ensure_ascii=False, indent=2)
        
        print(f"\n详细报告已保存到: comprehensive_200_test_report.json")


def main():
    """主函数"""
    print("启动全面的200+测试用例套件...")
    print("这将测试所有图表类型、过滤器、聚合、分段、排序等功能组合")
    
    tester = Comprehensive200Tests()
    
    # 生成测试用例
    tester.generate_all_test_cases()
    
    # 保存测试用例
    with open("comprehensive_200_test_cases.json", "w", encoding="utf-8") as f:
        json.dump(tester.test_cases, f, ensure_ascii=False, indent=2)
    
    print(f"测试用例已保存到: comprehensive_200_test_cases.json")
    
    # 运行测试
    tester.run_all_tests()
    
    print("\n🎯 全面测试套件完成!")


if __name__ == "__main__":
    main() 